#coding=utf-8
# Copyright 2011-2012 Maxim Petrov <maximpetrov@yahoo.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 3 or any later version.

import curses

class ModalWindow(object):
    """Абстракстный класс, от которого наследуют все модальные окна"""

    # Предпочитаемый минимальный размер окна
    _PREFERRED_MIN_WIDTH = 0
    _PREFERRED_MIN_HEIGHT = 0
    # Минимальный размер окна, если окно меньше, сообщение не выводится
    _MIN_WIDTH = 0
    _MIN_HEIGHT = 0
    # Отображается ли сообщение (влезает ли в экран)
    _message_visible = True
    # Кнопка, нажатие которой эквивалентно нажатию Esc
    _EXIT_BUTTON = u''
    # Увеличивать ли окно, если размер терминала позволяет
    _SHOULD_ENLARGE = True
    # Заголовок
    _title = u''
    # Панель, из которой состоит окно
    _window = None
    # Главное окно
    _main_win = None
    # Элементы окна, хранятся в списке в виде пар (элемент, получает_ли_фокус)
    _controls = []
    # Индекс активного элемента окна
    _selected_index = 0

    @property
    def _selected(self):
        """
        Возвращает выделенный элемент
        """

        if self._selected_index < len(self._controls):
            return self._controls[self._selected_index][0]
        else:
            return None

    @_selected.setter
    def _selected(self, selected):
        """
        Устанавливает выделенный элемент

        Аргументы:
            selected - элемент, который нужно сделать выделенным
        """

        for i, (c, f) in enumerate(self._controls):
            if selected == c:
                self._selected_index = i
                return

    def resize(self, window):
        """
        Функция изменения размера модального окна.

        Аргументы:

            window - окно в котором выводится модальное окно
        """

        # Вычисляем размер окна.  Возможны три варианта (независимо для
        # ширины и высоты):
        # 1. Максимальная ширина(высота) не больше предпочитаемой, тогда
        #    ширина(высота) устанавливается равной максимальной.
        # 2. Максимальная ширина(высота) более чем в 2 раза превышает
        #    предпочитаемую и разрешено увеличивать размеры окна, тогда
        #    устанавливаем ширину(высоту) равную половине максимальной.
        # 3. Максимальная ширина(высота) менее чем в 2 раза превышает
        #    предпочитаемую или запрещено увеличивать размеры окна,
        #    тогда устанавливаем ширину(высоту) равной предпочитаемой.
        max_height, max_width = window.getmaxyx()
        if max_width <= self._PREFERRED_MIN_WIDTH:
            width = max_width
        elif self._SHOULD_ENLARGE and \
             max_width >= self._PREFERRED_MIN_WIDTH * 2:
            width = max_width / 2
        else:
            width = self._PREFERRED_MIN_WIDTH
        if max_height <= self._PREFERRED_MIN_HEIGHT:
            height = max_height
        elif self._SHOULD_ENLARGE and \
             max_height >= self._PREFERRED_MIN_HEIGHT * 2:
            height = max_height / 2
        else:
            height = self._PREFERRED_MIN_HEIGHT
        # Проверяем не слишком ли маленькое окно получилось
        if height < self._MIN_HEIGHT or width < self._MIN_WIDTH:
            self._message_visible = False
            return
        else:
            self._message_visible = True
        # Изменяем размер модального окна и перемещаем его в центр,
        # перерисовываем окно: рисуем рамку и заголовок посередине
        y, x = window.getbegyx()
        self._window = window.subwin(height, width,
                                y + (max_height - height) / 2,
                                x + (max_width - width) / 2)
        self._window.clear()
        self._window.box()
        if len(self._title) <= width:
            self._window.addstr(0, (width - len(self._title)) / 2,
                                self._main_window.str_to_external(self._title))
        if len(self._controls) > 0:
            y, x = self._window.getbegyx()
            h, w = self._window.getmaxyx()
            for c, f in self._controls:
                c.draw(x, y, w, h)
            self._selected.focus = True
        self._window.refresh()

    def show(self, window, main_win):
        """
        Функция отображения модального окна.

        Аргументы:

            window - окно в котором выводится модальное окно
        """

        for c, f in self._controls:
            c.main_win = main_win
        self._main_win = main_win.window
        self._main_window = main_win
        # Выделяем необходимое подокно
        self.resize(window)

    def _next_control(self):
        """Переключение к следующему элементу"""

        if len(self._controls) == 0:
            return
        if self._selected_index >= len(self._controls):
            self._selected_index = 0
        else:
            self._selected.focus = False
            if self._selected_index == len(self._controls) - 1:
                i = 0
            else:
                i = self._selected_index + 1
            while i != self._selected_index:
                if self._controls[i][1]:
                    self._selected_index = i
                    break
                if i == len(self._controls) - 1:
                    i = 0
                else:
                    i += 1
        self._selected.focus = True

    def _previous_control(self):
        """Переключение к предыдущему элементу"""

        if len(self._controls) == 0:
            return
        if self._selected_index >= len(self._controls):
            self._selected_index = 0
        else:
            self._selected.focus = False
            if self._selected_index == 0:
                i = len(self._controls) - 1
            else:
                i = self._selected_index - 1
            while i != self._selected_index:
                if self._controls[i][1]:
                    self._selected_index = i
                    break
                if i == 0:
                    i = len(self._controls) - 1
                else:
                    i -= 1
        self._selected.focus = True

    def handle_keypress(self, ch, uni):
        """
        Функция отрабатывает нажатие клавиши.

        Аргументы:
            ch - код нажатой клавиши
            uni - признак того, что ch - является символом в юникоде

        Возвращаемое значение:
            Если нажатие этой клавиши привело к закрытию окна,
            то возвращает словарь параметров, иначе возвращает None
        """

        # Если нажат Esc
        if ch == 27:
            return {u'button': self._EXIT_BUTTON}
        else:
            return
